#ifndef __M_HELPER_H__
#define __M_HELPER_H__
#include <iostream>
#include <string>
#include <vector>
#include <sqlite3.h>

#include <iomanip>
#include <sstream>
#include <random>
#include <atomic>
#include <sys/stat.h>
#include <fstream>
#include "logger.hpp"
#include <cstring>
#include <cerrno>

namespace wzf{
class SqliteHelper
{
public:
    typedef int(*SqliteCallback)(void*, int, char**, char**);
    SqliteHelper(const std::string& dbfile): _dbfile(dbfile), _handler(nullptr)
    {}
    bool open(int safe_level = SQLITE_OPEN_FULLMUTEX)
    {
        int ret = sqlite3_open_v2(_dbfile.c_str(), &_handler, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE | safe_level, nullptr);
        if(ret != SQLITE_OK)
        {
            ELOG("创建/打开sqlite失败: %s\n", sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }
    bool exec(const std::string& sql, SqliteCallback cb, void* arg)
    { 
        int ret = sqlite3_exec(_handler, sql.c_str(), cb, arg, nullptr);
        if(ret != SQLITE_OK)
        {
            ELOG("%s sql语句执行失败: %s\n", sql.c_str(), sqlite3_errmsg(_handler));
            return false;
        }
        return true;
    }
    void close()
    {
        if(_handler) sqlite3_close_v2(_handler);
    }
private:
    std::string _dbfile;
    sqlite3* _handler; //句柄
};

class StrHelper
{
public:
    static size_t split(const std::string& str, const std::string& sep, std::vector<std::string>& result)
    {
        int end = 0, start = 0;
        while(start < str.size()) 
        {
            end = str.find(sep, start);
            if(end == std::string::npos)
            {
                result.push_back(str.substr(start));
                break;
            }
            if(end != start)
                result.push_back(str.substr(start, end - start));
            start = end + sep.size();
        }
        return result.size();
    }
};
class UUIDHelper
{
public:
    static std::string uuid()
    {
        std::random_device rd;
        std::mt19937_64 mt(rd()); 

        std::uniform_int_distribution<int> distribution(0, 255);

        std::stringstream ss;
        for (int i = 0; i < 8; i++)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << distribution(mt);
            if (i == 3 || i == 5 || i == 7)
                ss << '-';
        }
        static std::atomic<size_t> atomic(1); 
        size_t num = atomic.fetch_add(1);     
        for (int i = 7; i >= 0; i--)
        {
            ss << std::setw(2) << std::setfill('0') << std::hex << ((num >> 8 * i) & 0xff);
            if (i == 6)
                ss << '-';
        }
        return ss.str();
    }
};
class FileHelper
{
public:
    FileHelper(const std::string& filename)
        :_filename(filename)
    {}
    bool exists()
    {
        //access不具备跨平台性
        struct stat st;
        return stat(_filename.c_str(), &st) == 0;
    }
    size_t size()
    {
        struct stat st;
        int ret = stat(_filename.c_str(), &st);
        if(ret < 0)
        {
            ELOG("文件属性查看失败");
            return 1;
        }
        return st.st_size;
    }
    bool read(std::string& buffer)
    {
        size_t fsize = size();
        buffer.resize(fsize);    //不resize下面解引用0会报错，导致在一个非法地址写入数据
        // buffer = "ssssss";
        return read(&buffer[0], 0, fsize); //c_str返回的是const char* 
    }
    bool read(char* buffer, size_t pos, size_t len)
    {
        //1.打开文件
        std::ifstream ifs(_filename, std::ios::binary | std::ios::in);//std::ios::binary 防止特殊字符的特殊处理，导致和文件里的不一样
        if(ifs.is_open() == false)
        {
            ELOG("%s 文件打开失败", _filename.c_str());
            return false;
        }
        //2. 跳转指的读取位置
        ifs.seekg(pos, std::ios::beg); //读取指针（get pointer）
        //3. 开始读取
        ifs.read(buffer, len);   //必须先把空间开好，如果 buffer 的空间不够，ifs.read 会导致越界访问或者写入无效内存
        if(ifs.good() == false)
        {
            ELOG("%s 文件读取失败", _filename.c_str());
            ifs.close();
            return false;
        }
        //4. 关闭文件
        ifs.close();
        return true;
    }
    bool write(const std::string& buffer) //文件的写入都是覆盖式写入
    {
        return write(buffer.c_str(), 0, buffer.size()); 
    }
    bool write(const char* buffer, size_t pos, size_t len)
    {
        std::fstream fs(_filename, std::ios::binary | std::ios::in | std::ios::out);
        if(fs.is_open() == false)
        {
            ELOG("%s 文件打开失败", _filename.c_str());
            return false;
        }
        fs.seekp(pos, std::ios::beg); //写入指针（put pointer）

        fs.write(buffer, len);
        
        if(fs.good() == false)
        {
            ELOG("%s 文件写入失败", _filename.c_str());
            return false;
        }
        fs.close();
        return false;
    }
    static bool createFile(const std::string& filename) //以打开文件的方式来创建文件
    {
        std::fstream ofs(filename, std::ios::binary | std::ios::out);
        if(ofs.is_open() == false)
        {
            ELOG("%s 文件打开失败", filename.c_str());
            return false;
        }
        ofs.close();
        return false;
    }
    static bool removeFile(const std::string& filename)
    {
        return ::remove(filename.c_str());
    }
    static bool createDirectory(const std::string& path)
    {
        size_t tail, start = 0;
        while(start < path.size())
        {
            tail = path.find("/", start);
            if(tail == std::string::npos)
            {
                // std::string subpath = path.substr(start);  //最后一个链接不上
                // return mkdir(subpath.c_str(), 0775) == 0;
                return mkdir(path.c_str(), 0775) == 0;
            }
            std::string subpath = path.substr(0, tail); //多级目录，体现
            if(mkdir(subpath.c_str(), 0775) != 0 && errno != EEXIST) 
            {
                ELOG("%s 创建失败", subpath.c_str());
                return false;
            }
            start = tail + 1;
        }
        return true;
    }
    static bool removeDirectory(const std::string& path)
    {
        //rmdir必须是空的，那么就要倒着删除
        std::string cmd = "rm -rf " + path;
        return system(cmd.c_str()) != -1;
    }
    static const std::string getParentDirectory(const std::string& filename)
    {
        size_t pos = filename.find_last_of("/");
        if(pos == std::string::npos)
        {
            return "./";
        }
        std::string path = filename.substr(0, pos); //不以/结尾
        return path;
    }
    bool rename(const std::string& newname)
    {
        return ::rename(_filename.c_str(), newname.c_str()); //stdio.h 全局的作用域限定符
    }
private:
    std::string _filename;
};
}
#endif